package cn.tedu.adminsystem.webapi.filter;

import cn.tedu.adminsystem.webapi.security.LoginPrincipal;
import cn.tedu.community.commons.restful.JsonResult;
import cn.tedu.community.commons.restful.ServiceCode;
import com.alibaba.fastjson.JSON;
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

@Slf4j
@Component
public class JwtAuthorizationFilter extends OncePerRequestFilter {

    @Value("${community.jwt.secret-key}")
    private String secretKey;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

        // 清除SecurityContext中的数据
        SecurityContextHolder.clearContext();

        // 根据业内惯用的做法，客户端应该将JWT保存在请求头（Request Header）中的名为Authorization的属性中
        String jwt = request.getHeader("Authorization");
        log.debug("尝试接收客户端携带的JWT数据，JWT：{}", jwt);

        //判断客户端是否提交了有效的JWT
        if (!StringUtils.hasText(jwt) || jwt.length() < 113) {
            //直接放行
            filterChain.doFilter(request, response);
            // 【重要】终止当前方法的执行，不执行当前方法接下来的代码
            return;
        }

        //尝试解析JWT
        Claims claims = null;
        response.setContentType("application/json; charset=utf-8");
        try {
            claims = Jwts.parser()
                    .setSigningKey(secretKey)
                    .parseClaimsJws(jwt)
                    .getBody();
        }catch (SignatureException e){
            String message = "非法访问!";
            log.warn("解析JWT时出现SignatureException，响应消息：{}", message);
            JsonResult<Void> jsonResult
                    = JsonResult.fail(ServiceCode.ERR_JWT_SIGNATURE,message);
            PrintWriter printWriter = response.getWriter();
            printWriter.println(JSON.toJSONString(jsonResult));
            printWriter.close();
            return;
        }catch (MalformedJwtException e){
            String message = "非法访问!";
            log.warn("解析JWT时出现MalformedJwtException，响应消息：{}", message);
            JsonResult<Void> jsonResult
                    = JsonResult.fail(ServiceCode.ERR_JWT_MALFORMED,message);
            PrintWriter printWriter = response.getWriter();
            printWriter.println(JSON.toJSONString(jsonResult));
            printWriter.close();
            return;
        }catch (ExpiredJwtException e){
            String message = "您的登录信息已过期，请重新登录!";
            log.warn("解析JWT时出现ExpiredJwtException，响应消息：{}", message);
            JsonResult<Void> jsonResult
                    = JsonResult.fail(ServiceCode.ERR_JWT_EXPIRED,message);
            PrintWriter printWriter = response.getWriter();
            printWriter.println(JSON.toJSONString(jsonResult));
            printWriter.close();
            return;
        }
        Long id = claims.get("id",Long.class);
        String username = claims.get("username",String.class);
        String authoritiesJsonString = claims.get("authoritiesJsonString",String.class);
        log.debug("从JWT中解析得到的管理员ID：{}", id);
        log.debug("从JWT中解析得到的管理员用户名：{}", username);
        log.debug("从JWT中解析得到的管理员权限列表JSON：{}", authoritiesJsonString);

        // 将JSON格式的权限列表转换成Authentication需要的类型（Collection<GrantedAuthority>）
        List<SimpleGrantedAuthority> authorities =
                JSON.parseArray(authoritiesJsonString,SimpleGrantedAuthority.class);

        //基于解析JWT的结果创建认证信息
        LoginPrincipal principal = new LoginPrincipal();
        principal.setId(id);
        principal.setUsername(username);
        Object credentials = null; //应该为null
        Authentication authentication = new UsernamePasswordAuthenticationToken(
                principal, credentials, authorities
        );

        //将认证信息存入到SecurityContext中
        SecurityContext securityContext = SecurityContextHolder.getContext();
        securityContext.setAuthentication(authentication);

        // 过滤器链继承向后执行，即：放行
        // 如果没有执行以下代码，表示“阻止”，即此请求的处理过程到此结束，在浏览器中将显示一片空白
        filterChain.doFilter(request, response);
    }
}
